"""

.. module:: initialisationtask
.. moduleauthor:: @author: 

The purpose of this module is ... 
   
.. seealso:: modules :mod:`<module>`

*Constants*:
.. data:: constant_1
        
    constant_1 is used for ......
        
.. data:: constant_2
        
    constant_2 is used for ......
"""

# Imports
#
import httplib
import threading

from runtime.task import Task
from datastore import *
from services import *
from httpserver import HTTPThread

# from services.httpserver import *       # FIXME: not sure why I need this

# Constants
#

# Classes
#

class InitialisationTask(Task):
    """This class is a daemon task. It is responsible for stage 2 of node
    initialisation.

    Initialisation Stage 2:
    - user must enter correct admin password before being able to modify
      configurables.
    - user is prompted to input admin node (whose URL was entered earlier 
      by the IT server admin) credentials (for test or production):
      - council name
      - password
    - test the supplied credentials for the admin site and re-prompts if 
      there is a problem.
    - the list of available node types (for this user) are fetched from 
      the admin node.
    - user is prompted to select the node type (if a council):
      - adaptor, test and production
      - regression test, test and production (PVT only)
      - training, test and production
    - user is prompted to select the node type (if a vendor):
      - all council test node-types
      - reference implementation (test)
    - if node-type is adaptor or test or training:
      - SPEAR username
      - SPEAR password 
    - the node then tests login to SPEAR
    - if login to SPEAR fails node displays error and asks user to re-enter 
      SPEAR credentials. 
    - get hostnames of all configured tasks.
    
    *Usage examples*::

        example code

    *Common Arguments for Class methods*:
        <arguments>
    
    *Audience*:
        <audience>
    
    *Constants*:
        .. data:: constant_1
        
            constant_1 is used for ......
        
        .. data:: constant_2
        
            constant_2 is used for ......
    """
    
    # Class constants
    #
    STATE_START = 0
    STATE_GET_ADMINPASSWORD = 1
    STATE_GET_ADMINNODECREDENTIALS = 2
    STATE_GET_NODETYPE = 3
    STATE_GET_SPEARCREDENTIALS = 4
    STATE_GET_TASKHOSTNAMES = 5
    STATE_STOP = 99
    
    def __init__(self, ds, spaces, task_uri, task_def_rs):
        # <purpose>
        #
        # Parameters:
        #     ds : datastore.database.DataStore
        #     spaces : runtime.spaces.Spaces - Reference to spaces
        #     task_uri : String - the uri for this task. The root uri will most
        #                likely be the 'binding' uri for this task.
        #     task_def_rs : datastore.resource.Resource - Task definition resource.
        #
        
        Task.__init__(self, ds, spaces, task_uri, task_def_rs)
        
        self.logger = logging.getLogger('yarra.services.httptask.InitialisationTask')
        self.logger.log(logging.DEBUG, "ds (%s), spaces (%s), task_uri (%s), task_def_rs (%s)" % (ds, spaces, task_uri, task_def_rs.pretty_print()))
        
        self.state = InitialisationTask.STATE_START
        
    def cb_start(self, method, uri, new_uri, result, context, **kwargs):
        """This function is called when the task starts. 
        
        :param method: <class> or <type>
        :param uri: The task Uri.
        :param new_uri: <class> or <type>
        :param result: <class> or <type>
        :param context: <class> or <type>
        :param kwargs: <class> or <type>
        """
        
        self.logger.log(logging.DEBUG, "***************** uri (%s), new_uri (%s)" % (uri, new_uri))
        
        # All 'get' messages to this task are interpreted as requests for Spaces Resources.
        #
        self.spaces_context.process_all(uri.add("listen"), get='cb_handle_request', put='cb_handle_request')
        
    def cb_stop(self, method, uri, res, result, context, **kwargs):
        """This function will stop any listeners still running.
        
        :param method: <class> or <type>
        :param uri: The task Uri.
        :param res: <class> or <type>
        :param result: <class> or <type>
        :param context: <class> or <type>
        :param kwargs: <class> or <type>
        """
        
        self.logger.log(logging.DEBUG, "method (%s), uri (%s)" % (method, uri))
    
    def cb_handle_request(self, method, uri, new_uri, result, context, **kwargs):
        """This function handles all HTTP requests during initialisation.
        
        :param method: <class> or <type>
        :param uri: The task Uri.
        :param new_uri: <class> or <type>
        :param result: <class> or <type>
        :param context: <class> or <type>
        :param kwargs: <class> or <type>
        """
        
        self.logger.log(logging.DEBUG, "uri (%s), new_uri (%s), result (%s)" % (uri, new_uri, result))
        
        output_rs = Resource()
        
        if (self.state == InitialisationTask.STATE_START):
            # GET /
            #
            # 1. state = STATE_GET_ADMINPASSWORD
            # 2. create error Resource (no error)
            # 3. get the HTML template (administration password entry)
            # 4. generate HTML from template (arg = error Resource)
            # 5. create response Resource with HTML in Resource content.
            # 6. return response Resource.
            # 
            
            pass
        elif (self.state == InitialisationTask.STATE_GET_ADMINPASSWORD):
            # POST /adminpassword
            #
            # 1. get the administration password from the request
            # 2. validate password against configuration resource in datastore.
            # 3. if (not valid password)
            #    - state = no change
            #    - create error Resource (password failure) 
            #    - get the HTML template (administration password entry)
            #    - generate HTML from template (arg = error Resource)
            #    else
            #    - state = STATE_GET_ADMINNODECREDENTIALS
            #    - create error Resource (no error) 
            #    - get the HTML template (admin node credentials entry)
            #    - generate HTML from template (arg = error Resource)
            # 4. create response Resource with HTML in Resource content.
            # 5. return response Resource.
            #
            
            pass
        elif (self.state == InitialisationTask.STATE_GET_ADMINNODECREDENTIALS):
            # POST /adminnodecredentials
            #
            # 1. get the admin node credentials (council name and password) from the request
            # 2. validate admin node credentials by connecting to admin node
            #    and sending credentials to login page (?)
            # 3. if (not valid credentials)
            #    - state = no change
            #    - create error Resource (credentials failure) 
            #    - get the HTML template (admin node credentials entry)
            #    - generate HTML from template (arg = error Resource)
            #    else
            #    - retrieve node-types from admin node
            #    - state = STATE_GET_NODETYPE
            #    - create node-types Resource 
            #    - get the HTML template (node-type entry)
            #    - generate HTML from template (arg = node-types Resource)
            # 4. create response Resource with HTML in Resource content.
            # 5. return response Resource.
            #
            
            pass
        elif (self.state == InitialisationTask.STATE_GET_NODETYPE):
            # POST /nodetype
            #
            # 1. get the node-type from the request
            # 2. if node-type is adaptor or test or training
            #    - state = STATE_GET_SPEARCREDENTIALS
            #    - create error Resource (no error)
            #    - get the HTML template (SPEAR credentials entry)
            #    - generate HTML from template (arg = error Resource)
            #    else
            #    - state = STATE_GET_TASKHOSTNAMES
            #    - get the HTML template (task hostname entry)
            #    - generate HTML from template
            # 3. create response Resource with HTML in Resource content.
            # 4. return response Resource.
            #
            
            pass
        elif (self.state == InitialisationTask.STATE_GET_SPEARCREDENTIALS):
            # POST /spearcredentials
            #
            # 1. get the SPEAR credentials (user name and password) from the request
            # 2. validate SPEAR credentials by connecting to SPEAR and sending
            #    credentials to login page (?)
            # 3. if (not valid credentials)
            #    - state = no change
            #    - create error Resource (credentials failure) 
            #    - get the HTML template (SPEAR credentials entry)
            #    - generate HTML from template (arg = error Resource)
            #    else
            #    - fetch task details from datastore
            #    - state = STATE_GET_TASKHOSTNAMES
            #    - create tasks Resource 
            #    - get the HTML template (task hostname entry)
            #    - generate HTML from template (arg = tasks Resource)
            # 4. create response Resource with HTML in Resource content.
            # 5. return response Resource.
            #
            
            pass
        elif (self.state == InitialisationTask.STATE_GET_TASKHOSTNAMES):
            # POST /taskhostnames
            #
            # 1. get the task hostnames from the request
            # 2. validate task hostnames by checking that all are entered.
            # 3. if (not valid task hostnames)
            #    - state = no change
            #    - create error Resource (task hostname entry failure) 
            #    - get the HTML template (task hostname entry)
            #    - generate HTML from template (arg = error Resource)
            #    else
            #    - state = STATE_STOP
            #    - update configuration resource in datastore
            #    - rename datastore to '<node-type>.datastore'
            #    - update Windows Service to update the node's name/description
            #      with the node-type
            #    - backup configuration resource to admin node 
            #    - create configuration-complete Resource
            #    - get the HTML template (configuration-complete entry)
            #    - generate HTML from template (arg = configuration-complete Resource)
            # 4. create response Resource with HTML in Resource content.
            # 5. return response Resource.
            #
        
            pass
        
        elif (self.state == InitialisationTask.STATE_STOP):
            # GET or POST
            #
            # 1. state = no change
            # 2. create configuration-complete Resource
            # 3. get the HTML template (configuration-complete entry)
            # 4. generate HTML from template (arg = configuration-complete Resource)
            # 5. create response Resource with HTML in Resource content.
            # 6. return response Resource.
            #
            
            pass
        
        # Note: The convention for returning Resources from a server task (like 
        #       this) to clients is:
        #
        #       1. create a Uri based on the server task's Uri, appended with the
        #          parts:
        #          - "requests"
        #          - <uuid>
        #          - "response"
        #       2. put() the output Resource to Spaces using the Uri.
        #       3. return a copy of the Uri to the client, with only the first 2 
        #          parts, for them to request the result Resource from Spaces.
        #          The client will get_wait() on:
        #
        #          <response_uri>.add("response")
        # 
        #       A consequence of this is that the server task CANNOT process 
        #       requests sent to "<task_uri>.collection(True)" because they will
        #       process the response Resources. (Actually the server task will 
        #       block on Spaces.put() because the same task is waiting on the Uri.)
        #
        response_uri = self.spaces_context.uri(self.task_uri).add("requests").add(str(self.spaces_context.uuid()))
        self.spaces_context.put(response_uri.add("response"), output_rs)
        return (True, response_uri.uri_copy())